﻿#region [     文件信息     ]

/*----------------------------------------------------------------
 * CLR版本：4.0.30319.42000
 * 机器名称：VAMPIREWAL
 * 公司名称：Organization
 * 命名空间：Vampirewal.Admin.Upgrade
 * 唯一标识：3b74d279-5d57-4626-847b-ff3646a73f0f
 * 文件名：MainViewModel
 * 当前用户域：VAMPIREWAL
 *
 * 创建者：Administrator
 * 电子邮箱：1425271996@qq.com
 * 创建时间：2023/3/16 12:20:25
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/

#endregion [     文件信息     ]

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Input;
using TouchSocket.Core;
using TouchSocket.Rpc.TouchRpc;
using TouchSocket.Sockets;

namespace Vampirewal.Admin.Upgrade;

/// <summary>
///
/// </summary>
public partial class MainViewModel : NotifyBase
{
    public MainViewModel()
    {
        //构造函数
        //Title = "更新程序";

        //设置配置文件路径
        ConfigPath = $"{AppDomain.CurrentDomain.BaseDirectory}AppConfig.json";

        if (File.Exists(ConfigPath))
        {
            using (System.IO.StreamReader sr = System.IO.File.OpenText(ConfigPath))
            {
                string json = sr.ReadToEnd();

                var Config = JObject.Parse(json);

                _config = Config as JToken;
                JToken result2 = _config.DeepClone();

                Title = JSON_SeleteNode(_config, "UpdateOptions.UpdateName");

                string ServerIp = $"{JSON_SeleteNode(_config, "UpdateOptions.ServerIp")}:{JSON_SeleteNode(_config, "UpdateOptions.ServerPort")}";

                client = new TcpTouchRpcClient();

                client.Setup(new TouchSocketConfig().SetRemoteIPHost(ServerIp).SetVerifyToken(JSON_SeleteNode(_config, "UpdateOptions.AppToken")).SetVerifyTimeout(60000).SetRootPath(AppDomain.CurrentDomain.BaseDirectory).ConfigurePlugins(c =>
                {
                    c.Add<TouchRpcActionPlugin<HttpTouchRpcClient>>()
                     .SetFileTransfered((client, e) =>
                     {
                         if (e.Result.ResultCode == ResultCode.Success)
                         {
                             var aaa = e.FileInfo;
                         }
                     });
                }));

                client.Connect();

                sys_AppVersion = client.Invoke<Sys_AppVersion>("vampirewal.admin.server.rpcservices.service.systemservice.getlastversion", InvokeOption.WaitInvoke);
                if (sys_AppVersion != null)
                {
                    Attachment = client.Invoke<Sys_Attachment>("vampirewal.admin.server.rpcservices.service.systemservice.getattachmentbyid", InvokeOption.WaitInvoke, sys_AppVersion.BillId);
                }
            }
        }
    }

    #region [     属性     ]

    private string _Title;

    /// <summary>
    ///
    /// </summary>
    public string Title
    {
        get
        {
            return _Title;
        }
        set
        {
            _Title = value;
            DoNotify();
        }
    }

    private Sys_AppVersion _sys_AppVersion;

    public Sys_AppVersion sys_AppVersion
    {
        get { return _sys_AppVersion; }
        set { _sys_AppVersion = value; DoNotify(); }
    }

    private TcpTouchRpcClient client { get; set; }

    private JToken _config;

    private string ConfigPath;

    private bool _IsLoading;

    public bool IsLoading
    {
        get { return _IsLoading; }
        set { _IsLoading = value; DoNotify(); }
    }

    private Sys_Attachment _Attachment;

    /// <summary>
    ///
    /// </summary>
    public Sys_Attachment Attachment
    {
        get
        {
            return _Attachment;
        }
        set
        {
            _Attachment = value;
            DoNotify();
        }
    }

    #endregion

    #region [     Command命令     ]

    public RelayCommand UpdateCommand => new RelayCommand(ExcuteUpdateCommand);

    private async void ExcuteUpdateCommand()
    {
        IsLoading = true;

        string App = "App" + sys_AppVersion.Version + ".zip";

        string filename = $"{AppDomain.CurrentDomain.BaseDirectory}App{sys_AppVersion.Version}.zip";

        var rr = await Task<bool>.Run(async () =>
        {
            Metadata metadata = new Metadata();//传递到服务器的元数据
            metadata.Add("Operation", "下载新版本");
            //metadata.Add("BillId", BillId);
            //metadata.Add("UserId", BD_SystemDataContext.GetInstance.BDLoginUserInfo.BillId);
            //metadata.Add("UserName", BD_SystemDataContext.GetInstance.BDLoginUserInfo.name);
            //metadata.Add("Type", 0.ToString());
            //metadata.Add("FileSize", fileInfo.Length.ToString());

            //实例化本次传输的控制器，用于获取传输进度、速度、状态等。
            FileOperator fileOperator = new FileOperator()
            {
                Flags = TransferFlags.BreakpointResume,//尝试断点续传，使用断点续传时，会验证MD5值
                SavePath = $@"{App}",//保存路径
                ResourcePath = $@"{Attachment.FilePath}",//请求路径
                Metadata = metadata,//传递到服务器的元数据
                Timeout = TimeSpan.FromSeconds(60)
            };

            //此处的作用相当于Timer，定时每秒输出当前的传输进度和速度。
            LoopAction loopAction = LoopAction.CreateLoopAction(-1, 1000, (loop) =>
            {
                if (fileOperator.Result.ResultCode != ResultCode.Default)
                {
                    loop.Dispose();
                }

                client.Logger.Info($"进度：{fileOperator.Progress}，速度：{fileOperator.Speed()}");
            });

            await loopAction.RunAsync();

            //此方法会阻塞，直到传输结束，也可以使用PullFileAsync
            IResult result = await client.PullFileAsync(fileOperator);

            if (result.ResultCode == ResultCode.Success)
            {
                System.IO.Compression.ZipFile.ExtractToDirectory(filename, AppDomain.CurrentDomain.BaseDirectory, Encoding.UTF8, true); //解压

                Save(sys_AppVersion.Version);
                //Config.Save();

                return true;
            }

            return false;
        });

        IsLoading = false;

        if (rr)
        {
            System.Windows.Application.Current.Dispatcher.Invoke(new Action(() =>
            {
                File.Delete(filename);

                //打开自动更新程序
                Process m_Process = new Process();
                m_Process.StartInfo.FileName = $"{AppDomain.CurrentDomain.BaseDirectory}Vampirewal.Admin.Client.exe";
                m_Process.Start();

                System.Windows.Application.Current.Shutdown();
            }));
        }
    }

    #endregion

    #region 动态JSON处理

    /// <summary>
    /// 获取相应子节点的值
    /// </summary>
    /// <param name="childnodelist"></param>
    private static string JSON_SeleteNode(JToken json, string ReName)
    {
        try
        {
            string result = "";
            //这里6.0版块可以用正则匹配
            var node = json.SelectToken("$.." + ReName);
            if (node != null)
            {
                //判断节点类型
                if (node.Type == JTokenType.String || node.Type == JTokenType.Integer || node.Type == JTokenType.Float)
                {
                    //返回string值
                    result = node.Value<object>().ToString();
                }
            }
            return result;
        }
        catch (Exception ex)
        {
            return "";
        }
    }

    /// <summary>
    /// 遍历所以节点，替换其中某个节点的值
    /// </summary>
    /// <param name="jobj">json数据</param>
    /// <param name="nodeName">节点名</param>
    /// <param name="value">新值</param>
    private static void JSON_SetChildNodes(ref JToken jobj, string nodeName, string value)
    {
        try
        {
            JToken result = jobj as JToken;//转换为JToken
            JToken result2 = result.DeepClone();//复制一个返回值，由于遍历的时候JToken的修改回终止遍历，因此需要复制一个新的返回json
                                                //遍历
            var reader = result.CreateReader();
            while (reader.Read())
            {
                if (reader.Value != null)
                {
                    if (reader.TokenType == JsonToken.String || reader.TokenType == JsonToken.Integer || reader.TokenType == JsonToken.Float)
                    {
                        Regex reg = new Regex(@"" + nodeName + "$");
                        //SelectToken(Path)方法可查找某路径下的节点，在Newtonsoft.Json 4.5 版本中不可使用正则匹配，在6.0版本中可用使用，会方便很多，6.0版本下替换值会更方便，这个需要特别注意的
                        if (reg.IsMatch(reader.Path))
                        {
                            result2.SelectToken(reader.Path).Replace(value);
                        }
                    }
                }
            }
            jobj = result2;
        }
        catch (Exception ex)
        {
        }
    }

    /// <summary>
    /// json字符串格式化输出
    /// </summary>
    /// <param name="sourceJsonStr"></param>
    /// <returns></returns>
    public static string FormatJsonString(string sourceJsonStr)
    {
        //格式化json字符串
        JsonSerializer serializer = new JsonSerializer();
        TextReader tr = new StringReader(sourceJsonStr);
        JsonTextReader jtr = new JsonTextReader(tr);
        object obj = serializer.Deserialize(jtr);
        if (obj != null)
        {
            StringWriter textWriter = new StringWriter();
            JsonTextWriter jsonWriter = new JsonTextWriter(textWriter)
            {
                Formatting = Formatting.Indented,
                Indentation = 4,
                IndentChar = ' '
            };
            serializer.Serialize(jsonWriter, obj);
            return textWriter.ToString();
        }
        else
        {
            return sourceJsonStr;
        }
    }

    private void Save(string NewVersion)
    {
        JSON_SetChildNodes(ref _config, "AppVersion", NewVersion);

        var formatStr = FormatJsonString(JsonConvert.SerializeObject(_config));
        File.Delete(ConfigPath);

        File.AppendAllText(ConfigPath, formatStr);
    }

    #endregion
}

public class NotifyBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// 属性变化通知
    /// </summary>
    /// <param name="propName"></param>
    public void DoNotify([CallerMemberName] string propName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }

    /// <summary>
    /// 属性变化通知
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="field"></param>
    /// <param name="value"></param>
    /// <param name="propName"></param>
    public void Set<T>(ref T field, T value, [CallerMemberName] string propName = "")
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
        {
            return;
        }
        field = value;
        DoNotify(propName);
    }

    /// <summary>
    /// 带<paramref name="Action"/>的属性变化通知
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="field"></param>
    /// <param name="value"></param>
    /// <param name="OnChanged">属性变化的时候执行的方法</param>
    /// <param name="propName"></param>
    public void ActionSet<T>(ref T field, T value, Action<T> OnChanged = null, [CallerMemberName] string propName = "")
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
        {
            return;
        }

        field = value;
        OnChanged?.Invoke(value);
        DoNotify(propName);
    }
}

/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parameter"></param>
public delegate void DelegateCommand<T>(T parameter);

/// <summary>
///
/// </summary>
public delegate void DelegateCommand();

public class RelayCommand : ICommand
{
    /// <summary>
    ///
    /// </summary>
    /// <param name="command"></param>
    public RelayCommand(DelegateCommand command)
    {
        this.delCommand = command;
    }

    public RelayCommand(DelegateCommand command, Func<bool> canExcute)
    {
        this.delCommand = command;
        this._canExecute = canExcute;
    }

    private DelegateCommand delCommand;

    private readonly Func<bool> _canExecute;

    /// <summary>
    ///
    /// </summary>
    public event EventHandler CanExecuteChanged;

    /// <summary>
    /// 执行测试
    /// </summary>
    /// <param name="parameter"></param>
    /// <returns></returns>
    public bool CanExecute(object parameter)
    {
        if (delCommand == null)
        {
            return false;
        }

        return _canExecute == null ? true : _canExecute.Invoke();
    }

    /// <summary>
    /// 执行
    /// </summary>
    /// <param name="parameter"></param>
    public void Execute(object parameter)
    {
        if (CanExecute(parameter))
        {
            CanExecuteChanged?.Invoke(this, null);
            delCommand.Invoke();
        }
    }

    /// <summary>
    /// 刷新该命令是否能执行的状态
    /// </summary>
    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }
}

public partial class Sys_AppVersion : NotifyBase
{
    #region 属性

    private string _BillId;

    /// <summary>
    /// Id
    /// </summary>
    public string BillId
    {
        get
        {
            return _BillId;
        }
        set
        {
            _BillId = value;
            DoNotify();
        }
    }

    private DateTime? _CreateTime;

    /// <summary>
    /// 创建时间
    /// </summary>
    public DateTime? CreateTime
    {
        get
        {
            return _CreateTime;
        }
        set
        {
            _CreateTime = value;
            DoNotify();
        }
    }

    private string _CreateBy;

    /// <summary>
    /// 创建人
    /// </summary>
    public string CreateBy
    {
        get { return _CreateBy; }
        set { _CreateBy = value; DoNotify(); }
    }

    private string _CreateUserId;

    /// <summary>
    /// 创建人ID
    /// </summary>
    public string CreateUserId
    {
        get { return _CreateUserId; }
        set { _CreateUserId = value; DoNotify(); }
    }

    private DateTime? _UpdateTime;

    /// <summary>
    /// 更新时间
    /// </summary>
    public DateTime? UpdateTime
    {
        get
        {
            return _UpdateTime;
        }
        set
        {
            _UpdateTime = value;
            DoNotify();
        }
    }

    private string _UpdateBy;

    /// <summary>
    /// 更新人
    /// </summary>
    public string UpdateBy
    {
        get => _UpdateBy; set
        {
            _UpdateBy = value;
            DoNotify();
        }
    }

    private string _UpdateUserId;

    /// <summary>
    /// 更新人ID
    /// </summary>
    public string UpdateUserId
    {
        get { return _UpdateUserId; }
        set { _UpdateUserId = value; DoNotify(); }
    }

    #endregion

    /// <summary>
    /// 版本号1
    /// </summary>
    public string Version { get; set; }

    /// <summary>
    /// 描述
    /// </summary>
    public string Description { get; set; }

    /// <summary>
    /// 发布时间
    /// </summary>
    public DateTime PublishTime { get; set; }
}

/// <summary>
/// 附件表(BillId存各业务线Billid)
/// </summary>
public partial class Sys_Attachment
{
    public string BillId { get; set; }

    public string DtlId { get; set; }

    /// <summary>
    /// 文件名
    /// </summary>
    public string FileName { get; set; }

    /// <summary>
    /// 文件保存路径
    /// </summary>
    public string FilePath { get; set; }

    /// <summary>
    /// 文件大小
    /// </summary>
    public string FileSize { get; set; }

    /// <summary>
    /// 上传人ID
    /// </summary>
    public string UploaderId { get; set; }

    /// <summary>
    /// 上传人
    /// </summary>
    public string Uploader { get; set; }

    /// <summary>
    /// 上传时间
    /// </summary>
    public DateTime UploadTime { get; set; }

    /// <summary>
    /// 在服务器上的名称
    /// </summary>
    public string ServerName { get; set; } = "";
}